# -*- coding: utf-8 -*-

from time import sleep
import gtk, gtk.glade
import pygtk
import gobject
pygtk.require("2.0")
from arquitetura import SP, BP, TAM_MEM


class Widgets:
    """ Abre o arquivo XML do Glade e conecta os sinais """
    
    def __init__(self):
    
        self.widgets = gtk.glade.XML("interface.glade")
        self.widgets.signal_autoconnect(self)
        
        self.carrega_tabela_registradores()
        self.carrega_tabela_memoria()
        
        self.arquivo_aberto = None
        self.arquivo_modificado = False
        self.iniciou_exec = False
        
        self.linhas_mem = []
        self.linhas_reg = {}
        
        self.display = gtk.gdk.display_manager_get().get_default_display()
        self.clipboard = gtk.Clipboard(self.display, "PRIMARY")
        
        self.janela_principal.show_all()
        

    def __getattr__(self, widget_name):
        """ Retorna um atributo para um determinado objeto invocado """
        return self.widgets.get_widget(widget_name)
    
    
    def set_controle(self, controle):
        """ Faz a ligação com o controle do projeto """
        self.controle = controle
    
    
    def set_arquitetura(self, arquitetura):
        """ Faz a ligação com o arquitetura do projeto """
        self.arquitetura = arquitetura
    
    
    def set_montador(self, montador):
        """ Faz a ligação com a montador """
        self.montador = montador
    
    
    def carrega_tabela_registradores(self):
        """ Gera a árvore que mostra os registradores e os cabeçalhos """
        
        self.treemodel_reg = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
        self.arvore_rotulo.set_model(self.treemodel_reg)
        for i, coluna in enumerate(("Nome", "Valor")):
            renderer = gtk.CellRendererText()
            col = gtk.TreeViewColumn(coluna, renderer, text=i)
            self.arvore_rotulo.append_column(col)
        
    
    def atualizar_rotulos(self):
        """ Função para atualizar os registradores e rótulos na tela """
        pass
    
    
    def carrega_tabela_memoria(self):
        """ Gera a árvore que mostra os dados de memória e os cabeçalhos """
        
        self.treemodel_mem = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING,
                                           gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING,
                                           gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
        self.arvore_memoria.set_model(self.treemodel_mem)
        colunas = ("Endereço", "Valor (+0)", "Valor (+4)", "Valor (+8)",
                   "Valor (+c)", "Valor (+10)", "Valor (+14)", "Valor (+18)",
                   "Valor (+1c)")
        for i, coluna in enumerate(colunas):
            renderer = gtk.CellRendererText()
            col = gtk.TreeViewColumn(coluna, renderer, text=i)
            col.set_alignment(0.5)
            self.arvore_memoria.append_column(col)
    
    
    def carrega_tabela_mem(self):
        self.treemodel_mem.clear()
        col = 1
        endereco = 0
        self.linhas_mem.append(self.treemodel_mem.insert_before(None, None))
        self.treemodel_mem.set_value(self.linhas_mem[-1], 0, "%05X" % endereco)
        for i in range(0, len(self.arquitetura.mem), 4):
            
            if col % 9 == 0 and col != 0:
                self.linhas_mem.append(self.treemodel_mem.insert_before(None, None))
                endereco += 32
                self.treemodel_mem.set_value(self.linhas_mem[-1], 0, "%05X" % endereco)
                col = 1
            
            dado = "0x%08X" % self.arquitetura.get_mem_word(i)
            self.treemodel_mem.set_value(self.linhas_mem[-1], col, dado)
            col += 1
    
    
    def carrega_tabela_rotulo(self):
        self.treemodel_reg.clear()
        
        for item in ("pc", "hi", "lo"):
            self.linhas_reg[item] = self.treemodel_reg.insert_before(None, None)
            self.treemodel_reg.set_value(self.linhas_reg[item], 0, item)
            valor = "0x%08X" % eval("self.arquitetura.%s" % item)
            self.treemodel_reg.set_value(self.linhas_reg[item], 1, valor)
        
        for i, item in enumerate(["$"+str(i) for i in range(32)]):
            self.linhas_reg[item] = self.treemodel_reg.insert_before(None, None)
            self.treemodel_reg.set_value(self.linhas_reg[item], 0, item)
            valor = "0x%08X" % eval("self.arquitetura.r_regs[%d]" % i)
            self.treemodel_reg.set_value(self.linhas_reg[item], 1, valor)
        
        for k, v in self.montador.tabela_simbolo.items():
            self.linhas_reg[k] = self.treemodel_reg.insert_before(None, None)
            self.treemodel_reg.set_value(self.linhas_reg[k], 0, k)
            valor = "0x%08X" % self.arquitetura.get_mem_word(v)
            self.treemodel_reg.set_value(self.linhas_reg[k], 1, valor)
    
    
    def add_mem(self, offset, dado):
        offset /= 4
        self.treemodel_mem.set_value(self.linhas_mem[offset/8], (offset%8)+1, "0x%08X" % dado)
    
    
    def new_file(self, *args):
        codigo = self.codigo.get_buffer().set_text("")
        self.arquivo_modificado = False
        self.arquivo_aberto = None
        self.docname.set_text("Novo Documento")
    
    
    def open_file(self, *args):
        dialog = gtk.FileChooserDialog(title=None, action=gtk.FILE_CHOOSER_ACTION_OPEN,
                                        buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
        dialog.set_default_response(gtk.RESPONSE_OK)

        filter = gtk.FileFilter()
        filter.set_name("Código de montagem (*.asm)")
        filter.add_pattern("*.asm")
        dialog.add_filter(filter)
        
        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            arquivo = open(dialog.get_filename())
            self.arquivo_aberto = dialog.get_filename()
            codigo = arquivo.read()
            self.codigo.get_buffer().set_text(codigo)
            arquivo.close()
            self.docname.set_text(dialog.get_filename().split("/")[-1])
            self.arquivo_modificado = False
            self.carrega()   # Mudanças no ambiente
            
        dialog.destroy()
    
    
    def save_file(self, *args):
        if self.arquivo_aberto and self.arquivo_modificado:
            arquivo = open(self.arquivo_aberto, "w")
            buffer = self.codigo.get_buffer()
            codigo = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())
            arquivo.write(codigo)
            arquivo.close()
            nome = self.docname.set_text(self.docname.get_text()[:-2])
            self.arquivo_modificado = False
            self.carrega()   # Mudanças no ambiente
        else:
            self.saveas_file(args)
    
    
    def saveas_file(self, *args):
        dialog = gtk.FileChooserDialog(title=None, action=gtk.FILE_CHOOSER_ACTION_SAVE,
                                        buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK))
        dialog.set_default_response(gtk.RESPONSE_OK)

        filter = gtk.FileFilter()
        filter.set_name("Código de montagem (*.asm)")
        filter.add_pattern("*.asm")
        dialog.add_filter(filter)
        
        dialog.set_current_name(".asm")
        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            arquivo = open(dialog.get_filename(), "w")
            self.arquivo_aberto = dialog.get_filename()
            buffer = self.codigo.get_buffer()
            codigo = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())
            arquivo.write(codigo)
            arquivo.close()
            self.docname.set_text(dialog.get_filename().split("/")[-1])
            self.arquivo_modificado = False
            self.carrega()   # Mudanças no ambiente
        dialog.destroy()
    
    
    def code_diff(self, *args):
        if not self.arquivo_modificado:
            nome = self.docname.get_text()
            self.docname.set_text("%s *" % nome)
            self.arquivo_modificado = True
        
        a = self.codigo.get_buffer()
        b = a.get_insert()
        c = a.get_iter_at_mark(b)
        self.linha.set_text(str(c.get_line()+1))
    
    
    def mount_code(self, *args):
        if not self.iniciou_exec:
            if self.arquivo_aberto and not self.arquivo_modificado:
                res = self.montador.assemble_code(self.arquivo_aberto)
                if res:
                    self.montador.montado = True
                    self.bt_executar.set_sensitive(True)
                    self.bt_executar_passo.set_sensitive(True)
                    self.controle.add_msg("A Montagem foi concluída com sucesso!")
                    try:
                        self.linha_exec.set_text("%d" % self.montador.linhas[self.arquitetura.pc_ini])
                    except KeyError:
                        pass
                else:
                    self.controle.add_msg("A Montagem não foi concluída com sucesso.")
            else:
                self.controle.add_msg("ERRO: O arquivo deve estar salvo!")
    
    
    def run_code(self, *args):
        self.controle.n_exec = self.controle.ultima_inst()
        res = True
        while (res):
            #sleep(int(self.incremento.get_text()))
            res = self.run_step_code(args)
    
    
    def run_step_code(self, *args):
        """ Executa o código passo-a-passo a cada click do evento """
        
        if not self.arquitetura.running:
            self.controle.add_msg(">>> Inicio da Execução.")
            self.bt_executar.set_sensitive(False)
            self.bt_parar.set_sensitive(True)
            self.controle.n_exec = self.controle.ultima_inst()
            self.arquitetura.pc = self.arquitetura.pc_ini
            self.reiniciar()
            self.arquitetura.running = True
        
        self.arquitetura.exec_ciclo()
        try:
            self.linha_exec.set_text("%d" % self.montador.linhas[self.arquitetura.pc])
        except KeyError:
            pass
        
        if self.arquitetura.pc > self.controle.n_exec:
            self.controle.add_msg(">>> Final da Execução.")
            self.arquitetura.running = False
            self.bt_executar.set_sensitive(True)
            self.bt_parar.set_sensitive(False)
            return False
        
        return True
        
    
    def reiniciar(self):
        self.arquitetura.hi = 0
        self.arquitetura.lo = 0
        self.arquitetura.r_regs = [0] * 32
        self.arquitetura.r_regs[SP] = TAM_MEM * 1024 - 4
        self.arquitetura.r_regs[BP] = self.arquitetura.r_regs[SP]
        self.carrega_tabela_rotulo()
    
    
    def carrega(self, reset=True):
        self.arquitetura.reset()
        self.linhas_mem = []
        self.linhas_reg = {}
        self.montador.tabela_simbolo = {}
        self.carrega_tabela_mem()
        self.carrega_tabela_rotulo()
        self.montador.montado = False
        self.bt_executar.set_sensitive(False)
        self.bt_executar_passo.set_sensitive(False)
        self.bt_parar.set_sensitive(False)

    
    def para_execucao(self, *args):
        self.controle.add_msg(">>> Execução Interrompida.")
        self.arquitetura.running = False
        self.bt_executar.set_sensitive(True)
        self.bt_parar.set_sensitive(False)
        
    
    def limpar_mensagens(self, *args):
        buffer = self.mensagens.get_buffer().set_text("")
    
    def mostra_sobre(self, *args):
        self.janela_sobre.show()
    
    def fecha_sobre(self, *args):
        self.janela_sobre.hide()
        
    def copy(self, *args):
        print "copiou"
    
    def cut(self, *args):
        pass
    
    def paste(self, *args):
        pass
    
    def sair(self, *args):
        """ Sinal para matar a aplicação quando o aplicativo for encerrado """
        gtk.main_quit()
        
    
    def show(self):
        """ Inicia o gtk loop da interface """
        gtk.main()